/*
    Ypsilon Scheme System
    Copyright (c) 2004-2008 Y.FUJITA / LittleWing Company Limited.
    See license.txt for terms and conditions of use
*/

#ifndef MUTEX_H_INCLUDED
#define MUTEX_H_INCLUDED

#include "core.h"

#if _MSC_VER

    #if USE_CRITICAL_SECTION

        class mutex_t {

            mutex_t(const mutex_t&);
            mutex_t& operator=(const mutex_t&);

        public:

            CRITICAL_SECTION mutex;

          #if MTDEBUG
            int lock_count;
          #endif

            mutex_t() { /* should be blank */ }

            void init(bool recursive = false)
            {
              #if MTDEBUG
                lock_count = 0;
              #endif
                InitializeCriticalSection(&mutex);
            }

            void destroy()
            {
              #if MTDEBUG
                if (lock_count) {
                    fatal("internal error:%s:%u destroy() lock held(%d)", __FILE__, __LINE__, lock_count);
                }
              #endif
                DeleteCriticalSection(&mutex);
            }

            void lock()
            {
                EnterCriticalSection(&mutex);
              #if MTDEBUG
                lock_count++;
              #endif
            }

            void unlock()
            {
              #if MTDEBUG
                lock_count--;
                assert(lock_count >= 0);
              #endif
                LeaveCriticalSection(&mutex);
            }

            void verify_locked()
            {
              #if MTDEBUG
                if (lock_count == 0) {
                    fatal("internal error:%s:%u verify_locked() failed.", __FILE__, __LINE__);
                }
              #endif
            }
        };

    #else

        class mutex_t {

            mutex_t(const mutex_t&);
            mutex_t& operator=(const mutex_t&);

        public:

            HANDLE mutex;

          #if MTDEBUG
            int lock_count;
          #endif

            mutex_t() { /* should be blank */ }

            void init(bool recursive = false)
            {
              #if MTDEBUG
                lock_count = 0;
              #endif
                mutex = CreateMutex(NULL, FALSE, NULL);
                MTVERIFY(mutex);
            }

            void destroy()
            {
              #if MTDEBUG
                if (lock_count) {
                    fatal("internal error:%s:%u destroy() lock held(%d)", __FILE__, __LINE__, lock_count);
                }
              #endif
                MTVERIFY(CloseHandle(mutex));
            }

            void lock()
            {
                MTVERIFY(WaitForSingleObject(mutex, INFINITE) != WAIT_FAILED);
              #if MTDEBUG
                lock_count++;
              #endif
            }

            void unlock()
            {
              #if MTDEBUG
                lock_count--;
                assert(lock_count >= 0);
              #endif
                MTVERIFY(ReleaseMutex(mutex));
            }

            void verify_locked()
            {
              #if MTDEBUG
                if (lock_count == 0) {
                    fatal("internal error:%s:%u verify_locked() failed.", __FILE__, __LINE__);
                }
              #endif
            }
        };

    #endif

#else

    class mutex_t {

        mutex_t(const mutex_t&);
        mutex_t& operator=(const mutex_t&);

    public:

        pthread_mutex_t mutex;

      #if MTDEBUG
        int lock_count;
      #endif

        mutex_t() { /* should be blank */ }

        void init(bool recursive = false)
        {
          #if MTDEBUG
            lock_count = 0;
          #endif
            pthread_mutexattr_t attr;
            MTVERIFY(pthread_mutexattr_init(&attr));
            if (recursive) {
                MTVERIFY(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
            } else {
              #if MTDEBUG
                MTVERIFY(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK));
              #else
                MTVERIFY(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL));
              #endif
            }
            MTVERIFY(pthread_mutex_init(&mutex, &attr));
            MTVERIFY(pthread_mutexattr_destroy(&attr));
        }

        void destroy()
        {
          #if MTDEBUG
            if (lock_count) {
                fatal("internal error:%s:%u destroy() lock held(%d)", __FILE__, __LINE__, lock_count);
            }
          #endif
            MTVERIFY(pthread_mutex_destroy(&mutex));
        }

        void lock()
        {
            MTVERIFY(pthread_mutex_lock(&mutex));
          #if MTDEBUG
            lock_count++;
          #endif
        }

        void unlock()
        {
          #if MTDEBUG
            lock_count--;
            assert(lock_count >= 0);
          #endif
            MTVERIFY(pthread_mutex_unlock(&mutex));
        }

        void verify_locked()
        {
          #if MTDEBUG
            if (lock_count == 0) {
                fatal("internal error:%s:%u verify_locked() failed.", __FILE__, __LINE__);
            }
          #endif
        }
    };

#endif

class scoped_lock {
    scoped_lock(const scoped_lock&);
    scoped_lock& operator=(const scoped_lock&);
    mutex_t& m_lock;
public:
    scoped_lock(mutex_t& lock) : m_lock(lock) { m_lock.lock(); }
    ~scoped_lock() { m_lock.unlock(); }
};

#endif // MUTEX_H_INCLUDED
